home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 301-325 / disk_319 / cnewssrc / cnews.orig.lzh / relay / history.c < prev    next >
C/C++ Source or Header  |  1989-06-27  |  8KB  |  316 lines

  1. /*
  2.  * history file bashing
  3.  *
  4.  * B 2.10+ news pulls a dirty (and undocumented) trick and converts
  5.  * message-id's to lower case before using them as keys in the dbm file.
  6.  * We don't; you'll want to fix your news readers accordingly.
  7.  *
  8.  * B 2.10.3+ rnews puts out a leading space before received
  9.  * time if the article contains an Expires: header; tough.
  10.  * C news does this right instead of compatibly.
  11.  *
  12.  * The second history field is really two: time-received and Expires: value,
  13.  * separated by a tilde.  This is an attempt at partial compatibility with
  14.  * B news, in that C expire can cope with B news history files.
  15.  *
  16.  * There is no point to storing seek offsets in network byte order in the
  17.  * dbm file, since dbm files are machine-dependent and so can't be shared
  18.  * by dissimilar machines anyway.
  19.  */
  20.  
  21. #include <stdio.h>
  22. #include <sys/types.h>
  23. #include "libc.h"
  24. #include "news.h"
  25. #include "config.h"
  26. #include "fgetmfs.h"
  27. #include "headers.h"
  28. #include "article.h"
  29. #include "history.h"
  30. #include "msgs.h"
  31.  
  32. #define HISTNAME "history"    /* name of the history file in $NEWSCTL */
  33. #define FIELDSEP '\t'
  34. #define SUBFIELDSEP '~'
  35.  
  36. /* give 0 & 2 pretty, SVIDish names */
  37. #ifndef SEEK_SET
  38. #define SEEK_SET 0
  39. #define SEEK_END 2
  40. #endif
  41.  
  42. typedef struct {
  43.     char *dptr;
  44.     int dsize;
  45. } datum;
  46.  
  47. /* private data */
  48. static FILE *fp = NULL;
  49. static char *filename;        /* absolute name of the ascii history file */
  50. static boolean writable;
  51.  
  52. /* libdbm imports */
  53. extern int dbminit(), store();
  54. extern datum fetch();
  55.  
  56. /* forward decls */
  57. FORWARD datum getposhist();
  58. FORWARD void mkhistent(), sanitise(), subsanitise();
  59.  
  60. STATIC void
  61. histname()
  62. {
  63.     if (filename == NULL)
  64.         filename = strsave(ctlfile(HISTNAME));
  65. }
  66.  
  67. /*
  68.  * open the history files: ascii first, then dbm.
  69.  * Try a+ mode first, then r mode, as dbm(3) does nowadays,
  70.  * so that this routine can be used by any user to read history files.
  71.  */
  72. STATIC boolean
  73. openhist()
  74. {
  75.     histname();
  76.     if (fp == NULL) {
  77.         if ((fp = fopenclex(filename, "a+")) != NULL)
  78.             writable = YES;
  79.         else if ((fp = fopenwclex(filename, "r")) != NULL)
  80.             writable = NO;
  81.         /* else fp==NULL and fopenwclex just complained */
  82.  
  83.         if (fp != NULL && dbminit(filename) < 0) {
  84.             /* no luck. dbminit will have just honked */
  85.             (void) nfclose(fp);    /* close ascii file */
  86.             fp = NULL;        /* and mark it */
  87.         }
  88.     }
  89.     return fp != NULL;
  90. }
  91.  
  92. STATIC datum
  93. getposhist(msgid)        /* return seek offset of history entry */
  94. char *msgid;
  95. {
  96.     register char *clnmsgid;
  97.     datum msgidkey, keypos;
  98.  
  99.     msgidkey.dptr = NULL;
  100.     msgidkey.dsize = 0;
  101.     if (!openhist())
  102.         return msgidkey;
  103.     clnmsgid = strsave(msgid);
  104.     sanitise(clnmsgid);
  105.     msgidkey.dptr = clnmsgid;
  106.     msgidkey.dsize = strlen(clnmsgid) + 1;    /* include NUL */
  107.     keypos = fetch(msgidkey);        /* offset into ascii file */
  108.     free(clnmsgid);
  109.     return keypos;
  110. }
  111.  
  112. boolean
  113. alreadyseen(msgid)        /* return true if found in the data base */
  114. char *msgid;
  115. {
  116.     datum posdatum;
  117.  
  118.     posdatum = getposhist(msgid);
  119.     return posdatum.dptr != NULL;
  120. }
  121.  
  122. char *                /* NULL if no history entry */
  123. gethistory(msgid)        /* return existing history entry, if any */
  124. char *msgid;
  125. {
  126.     long pos = 0;
  127.     datum posdatum;
  128.  
  129.     posdatum = getposhist(msgid);
  130.     if (posdatum.dptr != NULL && posdatum.dsize == sizeof pos) {
  131.         static char *histent = NULL;
  132.  
  133.         memcpy((char *)&pos, posdatum.dptr, sizeof pos); /* align */
  134.         nnfree(&histent);
  135.         if (fseek(fp, pos, SEEK_SET) != -1 &&
  136.             (histent = fgetms(fp)) != NULL)
  137.             return histent;
  138.     }
  139.     return NULL;
  140. }
  141.  
  142. /*
  143.  * Return a pointer to the "files" field of a history entry.
  144.  * Side-effect: trims \n from the history entry.
  145.  */
  146. char *
  147. findfiles(histent)
  148. char *histent;
  149. {
  150.     register char *tabp;
  151.  
  152.     trim(histent);
  153.     /* find start of 2nd field (arrival~expiry) */
  154.     tabp = index(histent, FIELDSEP);
  155.     if (tabp == NULL)
  156.         return NULL;                /* mangled entry */
  157.     /* find start of 3rd field (files list) */
  158.     else if ((tabp = index(tabp + 1, FIELDSEP)) == NULL)
  159.         return NULL;            /* cancelled or expired art. */
  160.     else
  161.         return tabp + 1;
  162. }
  163.  
  164. /*
  165.  * Generate a history entry from art.
  166.  * The history entry will have tabs and newlines deleted from the
  167.  * interior of fields, to keep the file format sane.
  168.  * Optionally print the start of an "accepted" log file line (no \n)
  169.  * (transmit() prints site names).
  170.  */
  171. void
  172. history(art, startlog)
  173. register struct article *art;
  174. boolean startlog;
  175. {
  176.     register char *msgid, *expiry;
  177.     time_t now;
  178.  
  179.     msgid = strsave(nullify(art->h.h_msgid));
  180.     sanitise(msgid);    /* RFC 1036 forbids whitespace in msg-ids */
  181.     expiry = strsave(nullify(art->h.h_expiry));
  182.     sanitise(expiry);
  183.     subsanitise(expiry);
  184.  
  185.     if (startlog) {
  186.         timestamp(stdout, &now);
  187.         if (printf(" %s + %s", sendersite(nullify(art->h.h_path)),
  188.             msgid) == EOF)
  189.             fulldisk(art, "stdout");
  190.     } else
  191.         now = time(&now);
  192.     if (!openhist())
  193.         art->a_status |= ST_DROPPED;    /* fall through and return */
  194.     else if (!writable) {
  195.         (void) fprintf(stderr, "%s: no write permission on `%s'\n",
  196.             progname, filename);
  197.         art->a_status |= ST_DROPPED;
  198.     } else if (fseek(fp, 0L, SEEK_END) == -1) {
  199.         warning("can't seek to end of `%s'", filename);
  200.         art->a_status |= ST_DROPPED;
  201.     } else
  202.         mkhistent(art, msgid, now, expiry);
  203.     free(msgid);
  204.     free(expiry);
  205. }
  206.  
  207. /*
  208.  * Internal interface to generate a history file entry,
  209.  * assuming all sanity checking has been done already.
  210.  * Record the (msgid, position) pair in the data base.
  211.  *
  212.  * The fflush is crash-proofing.
  213.  */
  214. STATIC void
  215. mkhistent(art, msgid, now, expiry)
  216. register struct article *art;
  217. char *msgid, *expiry;
  218. time_t now;
  219. {
  220.     long pos;
  221.     datum msgidkey, posdatum;
  222.                                         
  223.     pos = ftell(fp);            /* get seek ptr for dbm */
  224.     if (fprintf(fp, "%s%c%ld%c%s", msgid, FIELDSEP, now, SUBFIELDSEP, expiry)
  225.         == EOF)
  226.         fulldisk(art, filename);
  227.     /* don't write 3rd field for cancelled but unseen articles */
  228.     if (art->a_files != NULL && art->a_files[0] != '\0')
  229.         if (fprintf(fp, "%c%s", FIELDSEP, art->a_files) == EOF)
  230.             fulldisk(art, filename);
  231.     (void) putc('\n', fp);
  232.     if (fflush(fp) == EOF)
  233.         fulldisk(art, filename);
  234.  
  235.     msgidkey.dptr = msgid;
  236.     msgidkey.dsize = strlen(msgid) + 1;    /* include NUL */
  237.     posdatum.dptr = (char *)&pos;
  238.     posdatum.dsize = sizeof pos;
  239. #ifdef NOSTOREVAL
  240.     /* original v7 dbm store() returned no value */
  241.     (void) store(msgidkey, posdatum);
  242. #else
  243.     if (store(msgidkey, posdatum) < 0)
  244.         fulldisk(art, filename);
  245. #endif
  246. }
  247.  
  248. /*
  249.  * Turn \n & FIELDSEP into ' ' in s.
  250.  */
  251. STATIC void
  252. sanitise(s)
  253. register char *s;
  254. {
  255.     for (; *s != '\0'; ++s)
  256.         if (*s == FIELDSEP || *s == '\n')
  257.             *s = ' ';
  258. }
  259.  
  260. /*
  261.  * Turn SUBFIELDSEP into ' ' in s.
  262.  */
  263. STATIC void
  264. subsanitise(s)
  265. register char *s;
  266. {
  267.     for (; *s != '\0'; ++s)
  268.         if (*s == SUBFIELDSEP)
  269.             *s = ' ';
  270. }
  271.  
  272. /*
  273.  * Generate a fake history file entry, given a message-id, an Expires:
  274.  * value, and a "file" list ("net.foo/123").
  275.  */
  276. statust
  277. fakehist(fkmsgid, fkexpiry, fkfiles)
  278. char *fkmsgid, *fkexpiry, *fkfiles;
  279. {
  280.     struct article art;
  281.  
  282.     artinit(&art);
  283.     art.h.h_msgid = fkmsgid;
  284.     art.h.h_expiry = fkexpiry;
  285.     art.a_files = fkfiles;
  286.     history(&art, STARTLOG);
  287.     return art.a_status;
  288. }
  289.  
  290. /*
  291.  * Append "group/artnumstr" to the file list in *art.
  292.  */
  293. void
  294. histupdfiles(art, group, artnumstr)
  295. register struct article *art;
  296. register char *group;
  297. register char *artnumstr;
  298. {
  299.     unsigned addlen = strlen(group)+STRLEN(SFNDELIM)+strlen(artnumstr)+1;
  300.  
  301.     art->a_filed = YES;            /* make a note */
  302.     if (art->a_files == NULL) {
  303.         art->a_files = nemalloc(addlen);
  304.         art->a_files[0] = '\0';
  305.     } else {
  306.         art->a_files = realloc(art->a_files, (unsigned)
  307.             strlen(art->a_files) + STRLEN(" ") + addlen);
  308.         if (art->a_files == NULL)
  309.             errunlock("can't grow a_files", "");
  310.         (void) strcat(art->a_files, " ");
  311.     }
  312.     (void) strcat(art->a_files, group);    /* normal case */
  313.     (void) strcat(art->a_files, SFNDELIM);
  314.     (void) strcat(art->a_files, artnumstr);
  315. }
  316.